/**
 * 数学工具类。
 */
export class MathUtil {
   /** 极小常量 */
   public static EPSILON = Math.pow(2, -52);
   /** 缺省精度 */
   public static PRECISION: number = 4;
   /** 缺省公差 */
   public static TOLERANCE: number = Math.pow(0.1, MathUtil.PRECISION);
   /** 弧度缺省精度 */
   public static RAD_PRECISION: number = 5;
   /** 弧度缺省公差 */
   public static RAD_TOLERANCE: number = Math.pow(0.1, MathUtil.RAD_PRECISION);
   /** 面积缺省精度 */
   public static AREA_PRECISION: number = 6;
   /** 面积缺省公差 */
   public static AREA_TOLERANCE: number = Math.pow(0.1, MathUtil.AREA_PRECISION);
   /** 角度缺省公差(单位度) */
   public static ANGLE_TOLERANCE: number = 0.1;
   /** 数据常量 */
   public static PI = Math.PI;
   public static PI2 = Math.PI * 2;
   public static PI_2 = Math.PI * 0.5;
   public static PI_2P = 1 / (Math.PI * 0.5);
   public static PI_4 = Math.PI * 0.25;
   public static PI_8 = Math.PI * 0.125;
   /** 角度转换常量 */
   public static PI_P270 = Math.PI * 1.5;
   public static PI_P180 = Math.PI;
   public static PI_P90 = Math.PI * 0.5;
   public static PI_0 = 0;
   public static PI_N90 = -Math.PI * 0.5;
   public static PI_N180 = -Math.PI;
   public static PI_N270 = -Math.PI * 1.5;
   /** 角度转换常量 */
   public static DEG2RAD = Math.PI / 180;
   public static RAD2DEG = 180 / Math.PI;
   public static RADIAN_RATE = 180 / Math.PI;
   public static DEGREE_RATE = Math.PI / 180;
   /** 临时数组 */
   public static VALUE_1: Array<number> = new Array<number>(1);
   public static VALUE_2: Array<number> = new Array<number>(2);
   public static VALUE_3: Array<number> = new Array<number>(3);
   public static VALUE_4: Array<number> = new Array<number>(4);
   public static VALUE_9: Array<number> = new Array<number>(9);
   public static VALUE_12: Array<number> = new Array<number>(12);
   public static VALUE_16: Array<number> = new Array<number>(16);

   /**
    * 判断内容是否为零。
    *
    * @param value 内容
    * @return 是否为零
    */
   public static isEpsilon(value: number): boolean {
      return Math.abs(value) <= MathUtil.EPSILON;
   }

   /**
    * 判断内容是否为零。
    *
    * @param value 内容
    * @param tolerance 公差
    * @return 是否为零
    */
   public static isZero(value: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      if (isNaN(value)) {
         return true;
      }
      return Math.abs(value) <= tolerance;
   }

   /**
    * 判断内容是否为非零。
    *
    * @param value 内容
    * @param tolerance 公差
    * @return 是否为零
    */
   public static isNotZero(value: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      if (isNaN(value)) {
         return false;
      }
      return Math.abs(value) > tolerance;
   }

   /**
    * 判断数据是否2的幂。
    *
    * @param value 数据
    * @return 是否2的幂
    */
   public static isPowerOfTwo(value: number): boolean {
      return (value & (value - 1)) == 0 && value != 0;
   }

   /**
    * 判断内容是否近似相等。
    *
    * @param value1 内容1
    * @param value2 内容2
    * @param tolerance 公差
    * @return 是否相等
    */
   public static nearlyEquals(value1: number, value2: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      return Math.abs(value1 - value2) <= tolerance;
   }

   /**
    * 判断内容是否近似大于等于。
    *
    * @param value1 内容1
    * @param value2 内容2
    * @param tolerance 公差
    * @return 是否大于等于
    */
   public static nearlyGreaterEquals(value1: number, value2: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      return this.nearlyEquals(value1, value2, tolerance) || value1 > value2;
   }

   /**
    * 判断内容是否近似小于等于。
    *
    * @param value1 内容1
    * @param value2 内容2
    * @param tolerance 公差
    * @return 是否小于等于
    */
   public static nearlyLessEquals(value1: number, value2: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      return this.nearlyEquals(value1, value2, tolerance) || value1 < value2;
   }

   /**
    * 判断角度是否近似相等。
    *
    * @param value1 角度1
    * @param value2 角度2
    * @param tolerance 公差
    * @return 是否近似相等
    */
   public static nearlyEqualAngles(value1: number, value2: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      return this.nearlyEqualRadians(this.degToRad(value1), this.degToRad(value2), this.degToRad(tolerance));
   }

   /**
    * 判断弧度是否近似相等。
    *
    * @param value1 弧度1
    * @param value2 弧度2
    * @param tolerance 公差
    * @return 是否近似相等
    */
   public static nearlyEqualRadians(value1: number, value2: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      var pi2 = this.PI2;
      value1 %= pi2;
      value2 %= pi2;
      if (value1 < 0) {
         value1 += pi2;
      }
      if (value2 < 0) {
         value2 += pi2;
      }
      if (Math.abs(value1 - value2) <= tolerance) {
         return true;
      }
      if (value1 < value2) {
         value1 += pi2;
      } else {
         value2 += pi2;
      }
      if (Math.abs(value1 - value2) <= tolerance) {
         return true;
      }
      return false;
   }

   /**
    * 判断内容是否近似小于。
    *
    * @param value1 内容1
    * @param value2 内容2
    * @param tolerance 公差
    * @return 是否小于
    */
   public static nearlyLess(value1: number, value2: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      return this.nearlyEquals(value1, value2, tolerance) || value1 < value2;
   }

   /**
    * 判断内容是否近似大于。
    *
    * @param value1 内容1
    * @param value2 内容2
    * @param tolerance 公差
    * @return 是否大于
    */
   public static nearlyGreater(value1: number, value2: number, tolerance: number = MathUtil.TOLERANCE): boolean {
      return this.nearlyEquals(value1, value2, tolerance) || value1 > value2;
   }

   /**
    * 获得最接近2的幂的数值。
    *
    * @param value 数值
    * @return 数值
    */
   public static nearestPowerOfTwo(value: number): number {
      return Math.pow(2, Math.round(Math.log(value) / Math.LN2));
   }

   /**
    * 获得下个2的幂数值。
    *
    * @param value 数值
    * @return 数值
    */
   public static nextPowerOfTwo(value: number): number {
      value--;
      value |= value >> 1;
      value |= value >> 2;
      value |= value >> 4;
      value |= value >> 8;
      value |= value >> 16;
      value++;
      return value;
   }

   /**
    * 计算参数中的最小值。
    *
    * @param arguments 数组
    * @return 最小值
    */
   public static min(p1?: any, p2?: any, p3?: any, p4?: any, p5?: any, p6?: any) {
      var result = 0;
      var count = arguments.length;
      if (count > 1) {
         result = Number.MAX_VALUE;
         for (var i = 0; i < count; i++) {
            var value = arguments[i];
            if (value < result) {
               result = value;
            }
         }
      }
      return result;
   }

   /**
    * 计算参数中的最大值。
    *
    * @param arguments 数组
    * @return 最大值
    */
   public static max(p1?: any, p2?: any, p3?: any, p4?: any, p5?: any, p6?: any) {
      var result = 0;
      var count = arguments.length;
      if (count > 1) {
         result = Number.MIN_VALUE;
         for (var i = 0; i < count; i++) {
            var value = arguments[i];
            if (value > result) {
               result = value;
            }
         }
      }
      return result;
   }

   /**
    * 计算数值的符号位。
    *
    * @param value 数值
    * @return 符号
    */
   public static sign(value): number {
      if (value > 0) {
         return 1;
      } else if (value < 0) {
         return -1;
      }
      return 0;
   }

   /**
    * 转换角度为弧度。
    *
    * @param angle 角度
    * @return 弧度
    */
   public static degToRad(angle: number): number {
      return angle * MathUtil.DEGREE_RATE;
   }

   /**
    * 转换弧度为角度。
    *
    * @param rad 弧度
    * @return 角度
    */
   public static radToDeg(rad: number): number {
      return rad * MathUtil.RADIAN_RATE;
   }

   /**
    * 计算两个数字的插值。
    */
   public static lerp(a: number, b: number, rate: number) {
      return a + rate * (b - a);
   }

   /**
    * 计算在范围间的有效数字。
    *
    * @param value 内容
    * @param min 最小值
    * @param max 最大值
    * @return 范围数字
    */
   public static clamp(value: number, min: number, max: number): number {
      if (value > max) {
         value = max;
      }
      if (value < min) {
         value = min;
      }
      return value;
    }

   /**
    * 获得一个在0~360的度数。
    *
    * @param value 度数
    * @return 度数
    */
   public static getAngle360(value: number): number {
      var result = value % 360;
      if (MathUtil.isZero(result)) {
         result = 0;
      } else if (result < 0) {
         result += 360;
      }
      return result;
   }

   /**
    * 获得一个在-PI~PI的度数。
    *
    * @param value 度数
    * @return 度数
    */
   public static toRadInPI(value: number): number {
      var result = value % MathUtil.PI2;
      if (result > MathUtil.PI) {
         result -= MathUtil.PI2;
      }
      else if (result < -MathUtil.PI) {
         result += MathUtil.PI2;
      }
      return result;
   }

   /**
    *
    * @param n
    * @param m
    */
   public static euclideanModulo(n, m) {
      return ((n % m) + m) % m;
   }

   /**
    * 在b1—b2的范围内按x的比例在a1—a2的线性缩放x的值
    * @param x 值
    */
   public static mapLinear(x, a1, a2, b1, b2) {
      //  AssertUtil.debugFalse(a1!=a2);
      return b1 + (x - a1) * (b2 - b1) / (a2 - a1);
   }

   public static smoothstep(x, min, max) {
      if (x <= min) {
         return 0;
      }
      if (x >= max) {
         return 1;
      }
      x = (x - min) / (max - min);
      return x * x * (3 - 2 * x);
   }

   public static smootherstep(x, min, max) {
      if (x <= min) {
         return 0;
      }
      if (x >= max) {
         return 1;
      }
      x = (x - min) / (max - min);
      return x * x * x * (x * (x * 6 - 15) + 10);
   }

   /**
    * 产生一个介于[0,1]的随机浮点数
    */
   public static random16() {
      return Math.random();
   }

   /**
    *随机产生一个介于[low,high]之间的整数
    *
    *@param low 下限
    *@param high 上限
    *@return 随机整数
    */
   public static randInt(low: number, high: number): number {
      return low + Math.floor(Math.random() * (high - low + 1));
   }

   /**
    *随机产生一个介于[low,high]之间的浮点数
    *
    *@param low 下限
    *@param high 上限
    *@return 随机浮点数
    */
   public static randFloat(low: number, high: number): number {
      return low + Math.random() * (high - low);
   }

   /**
    * 产生一个介于[-range,range]之间的浮点数
    * @param range 范围
    * @return 随机浮点数
    */
   public static randFloatSpread(range: number): number {
      return range * (0.5 - Math.random());
   }

   public static roundDecimal(num: number, digit: number = 3) {
      var result = null,
         translateNum = 10 ** digit;

      if (num > 0) {
         result = Math.round(num * translateNum) / translateNum;
      }
      else {
         result = -Math.round(-num * translateNum) / translateNum;
      }

      return result;
   }

   /**
    *  已知三角形两条边和夹角,
    * @param b
    * @param c
    * @param A
    */
   public static triangleThirdEdge(b: number, c: number, A: number) {
      return Math.sqrt(b * b + c * c - 2 * b * c * Math.cos(A));
   }
   /**
   * 已知三角形两条边和夹角,
   * @param a
   * @param b
   * @param aa c边对应角
   */
   public static triangleThirdEdge2(a: number, b: number, A: number) {
      var h = b * Math.sin(A);
      var ad = b * Math.cos(A);
      var bd = Math.sqrt(a * a - h * h);
      var c = ad + bd;
      return c;
   }
}
